#include "elf.h"
#include "elf_loader.h"
#include "gdt.h"
#include "stdio.h"

static void elf_load_segment_to_memory(const int fd,
				       const Elf32_Phdr* phdr)
{
  t_uint32 i;

  seek(fd, phdr->p_offset, SEEK_SET);

  for (i = 0; i < phdr->p_filesz; ++i)
    read(fd, (char*)(((char*)phdr->p_vaddr) + i), 1);

  /* for (i = phdr->p_filesz; i < (phdr->p_memsz - phdr->p_filesz); ++i) */
  /*   *((char*)(phdr->p_vaddr + i)) = 0; */
}

void* elf_load(const char* elf_file,
	       t_uint32* stack_begin) // out
{
  int fd = open(elf_file, O_RDONLY);
  t_uint32 i;

  t_uint32 code_base_addr = 0;
  t_uint32 code_limit = 0;
  t_uint32 data_base_addr = 0;
  t_uint32 data_limit = 0;

  Elf32_Ehdr elf_header;

  read(fd, &elf_header, sizeof (Elf32_Ehdr));

  Elf32_Phdr program_header;

  seek(fd, elf_header.e_phoff, SEEK_SET);

  for (i = 0; i < elf_header.e_phnum; ++i)
    {
      read(fd, &program_header, sizeof (Elf32_Phdr));
      
      if (!(program_header.p_type & PT_LOAD))
      	continue;

      elf_load_segment_to_memory(fd, &program_header);

      if ((!code_base_addr) && (program_header.p_flags & PF_X))
	{
	  code_base_addr = program_header.p_vaddr;
	  code_limit = program_header.p_memsz;
	}
      else if ((!data_base_addr) && (program_header.p_flags & PF_W))
	{
	  data_base_addr = program_header.p_vaddr;
	  data_limit = program_header.p_memsz;
	  *stack_begin = data_limit;
	}
    }

  gdt_init_userland(code_base_addr,
		    code_limit,
		    data_base_addr,
		    data_limit);

  close(fd);

  return (void*) code_base_addr;
}

void jmp_to_userland(t_uint32 stack_begin,
		     t_uint32 jmp_addr)
{
  asm volatile ("pushl $0x20\n\t"
		"pushl %0\n\t"
		"pushf\n\t"
		"pushl $0x18\n\t"
		"pushl %1\n\t"
		"movw $0x20, %%ax\n\t"
		"movw %%ax, %%ds\n\t"
		"iret\n\t"
		:
		: "m" (stack_begin), "m" (jmp_addr)
		:);
}
